#include <stdio.h>
#include <assert.h>
#include <string.h>

#include <libxml/parser.h>
#include <libxml/xmlschemas.h>

#include <xmlsec/xmltree.h>
#include <xmlsec/xmldsig.h>

#include "common.h"

void extractKey(xmlSecKeyPtr key, xmlNodePtr node) {
  xmlSecKeyInfoCtxPtr keyInfo;

  keyInfo = xmlSecKeyInfoCtxCreate(NULL);
  assert(keyInfo);
  assert(!xmlSecKeyInfoNodeRead(node, key, keyInfo));
  xmlSecKeyInfoCtxDestroy(keyInfo);
}

static GSList *configs;

void load_config(char *file) {
  xmlDocPtr doc;
  xmlNodePtr root, cur;
  xmlSchemaParserCtxtPtr parser;
  xmlSchemaValidCtxtPtr vctx;
  xmlSchemaPtr schemap;
  
  doc = xmlParseFile(file);
  assert(doc);
  root = xmlDocGetRootElement(doc);
  assert(root);

  parser = xmlSchemaNewParserCtxt(DATADIR "/config.xsd");
  assert(parser);
  schemap = xmlSchemaParse(parser);
  assert(schemap);
  vctx = xmlSchemaNewValidCtxt(schemap);
  assert(vctx);
  assert(!xmlSchemaValidateDoc(vctx, doc));

  cur = doc->xmlChildrenNode;
  if(cur && !xmlStrcmp(xmlSecNodeGetName(cur), "configList"))
    cur = cur->xmlChildrenNode;
  if(cur)
    do {
      if(!xmlStrcmp(xmlSecNodeGetName(cur), "config")) {
	xmlNodePtr confptr = cur->xmlChildrenNode;
	struct config *cfg = calloc(1, sizeof(*cfg));
	
	cfg->map = g_hash_table_new(g_str_hash, g_str_equal);

	if(confptr)
	  do {
	    if(!xmlStrcmp(xmlSecNodeGetName(confptr), "root")) {
	      xmlNodePtr rootptr = confptr->xmlChildrenNode;

	      if(rootptr)
		do {
		  if(!xmlStrcmp(xmlSecNodeGetName(rootptr), "KeyInfo")) {
		    if(cfg->root)
		      fprintf(stderr, "Root the second?\n");
		    else {
		      cfg->root = xmlSecKeyCreate();
		      extractKey(cfg->root, rootptr);
		    }
		  } else 
		    ;
		} while((rootptr = rootptr->next));
	      else
		cfg->root = NULL;
	    } else if(!xmlStrcmp(xmlSecNodeGetName(confptr), "permit")) {
	      xmlNodePtr permitptr = confptr->xmlChildrenNode;

	      if(permitptr)
		do {
		  g_hash_table_insert(cfg->map, (gpointer)xmlSecNodeGetName(permitptr), (gpointer)2);
		} while((permitptr = permitptr->next));
	    } else if(!xmlStrcmp(xmlSecNodeGetName(confptr), "verify")) {
	      xmlNodePtr verifyptr = confptr->xmlChildrenNode;

	      if(verifyptr)
		do {
		    g_hash_table_insert(cfg->map, (gpointer)xmlSecNodeGetName(verifyptr), (gpointer)1);
		} while((verifyptr = verifyptr->next));
	    } else if(!xmlStrcmp(xmlSecNodeGetName(confptr), "condition")) {
	      fprintf(stderr, "Have condition!\n");
	      cfg->condition = confptr;
	    } else
	      ;
	  } while((confptr = confptr->next));
	fprintf(stderr, "attaching %p to %p\n", cfg, configs);
	configs = g_slist_append(configs, cfg);
      } else
	;
    } while((cur = cur->next));
}

struct cfgState {
  GSList *ret;
  struct trustState *trust;
  xmlSecKeyPtr key;
};

static void matchKey(gpointer cfgptr, gpointer csptr) {
  struct config *cfg = cfgptr;
  struct cfgState *cs = csptr;
  
  //  fprintf(stderr, "matchKey %p in %p\n", cfg, cs->ret);
  if(!cfg->root || findPath(cs->trust, cfg->root, cs->key))
    cs->ret = g_slist_append(cs->ret, cfg);
  //  fprintf(stderr, "matchKey %p out %p\n", cfg, cs->ret);
}

GSList *configsForKey(struct trustState *trust, xmlSecKeyPtr key) {
  struct cfgState c;
  
  if(!configs)
    load_config(SYSCONFDIR "/config.xml");
  memset(&c, 0, sizeof(c));
  c.trust = trust;
  c.key = key;
  g_slist_foreach(configs, matchKey, &c);
  return c.ret;
}
